package configs

import (
	"crypto/sha256"
	"encoding/json"
	"fmt"
	"regexp"
	"time"
	"errors"
	"strings"
    "gorm.io/datatypes"
    "github.com/go-playground/validator/v10"
	"gorm.io/gorm"
)

var (
	validTableNameRegex = regexp.MustCompile(`^[a-z][a-z0-9_]{0,63}$`)
	validColumnRegex = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_]{0,31}$`)
)

//生成表名
func GeneratePhysicalTableName(formName, suffix string) string {
	h := sha256.New()
	h.Write([]byte(formName + suffix))
	hash := fmt.Sprintf("%x", h.Sum(nil))
	return fmt.Sprintf("form_%s", hash[:8])
}

// 解析字段属性
func ParseFieldConfig(fields datatypes.JSON) (map[string]FieldConfig, error) {
	var zdsz map[string]FieldConfig
	if err := json.Unmarshal([]byte(fields), &zdsz); err != nil {
	    fmt.Println(err)
		return nil, err
	}
	return zdsz, nil
}

//检验字段
func ValidateFieldConfigs(bgsx FormSchema) (map[string]FieldConfig, error) {
    configs, err := ParseFieldConfig(bgsx.Fields)
	if err != nil {
		return configs, fmt.Errorf("字段属性错误: %v", err)
	}
	var cxtj []string
	if err := json.Unmarshal([]byte(bgsx.Cxtj), &cxtj); err != nil {
        return configs, fmt.Errorf("查询条件配置解析失败: %v", err)
    }
    seenColumns := make(map[string]struct{})
    for key, cfg := range configs {
        if !validColumnRegex.MatchString(key) {
            return configs, fmt.Errorf("字段名错误: %s", key)
        }

        if _, exists := seenColumns[key]; exists {
            return configs, fmt.Errorf("字段名重复: %s", key)
        }
        seenColumns[key] = struct{}{}
        selectzd := []string{"select", "radio", "muselect", "checkbox"}
        if InArray(cfg.Type, selectzd) && strings.TrimSpace(cfg.Options) == "" {
            return configs, fmt.Errorf("下拉选择必须设置选项: %s", key)
        }
        if isImageField(cfg.Type) && len(cxtj) > 0 && InArray(key, cxtj) {
            return configs, fmt.Errorf("图片、多选、多行文本不能作为查询条件: %s", key)
        }
        // 显示条件验证
        if cfg.Condition != nil {
            cond := cfg.Condition
            // 检查目标字段是否存在
            if _, exists := configs[cond.Field]; !exists {
                return configs, fmt.Errorf("字段[%s]的监听字段不存在: %s", key, cond.Field)
            }
            // 检查动作有效性
            if cond.Action != "show" && cond.Action != "hide" {
                return configs, fmt.Errorf("字段[%s]的条件动作必须为show/hide", key)
            }
        }
    }
    return configs, nil
}

// 校验表单预约配置
func ValidateFormSchema(form FormSchema) error {
    if form.IsReservation == true && len(form.Reservation) > 0 && string(form.Reservation) != "null" {
        var reservationCfg ReservationConfig
        if err := json.Unmarshal(form.Reservation, &reservationCfg); err != nil {
            return fmt.Errorf("预约配置解析失败: %v", err)
        }
        if err := validateReservationConfig(reservationCfg); err != nil {
            return err
        }
    }
    return nil
}

// 预约配置校验器（值接收）
func validateReservationConfig(rc ReservationConfig) error {
    validate := validator.New()
    validate.RegisterValidation("timeformat", func(fl validator.FieldLevel) bool {
        _, err := time.Parse("15:04", fl.Field().String())
        return err == nil
    })
    // 基础校验
    if err := validate.Struct(rc); err != nil {
        var errs validator.ValidationErrors
        if errors.As(err, &errs) {
            for _, e := range errs {
                switch e.Tag() {
                case "required":
                    return fmt.Errorf("字段 %s 必须填写", e.Field())
                case "oneof":
                    return fmt.Errorf("预约模式必须是 dynamic/static/custom 之一")
                case "timeformat":
                    return fmt.Errorf("字段 %s 时间格式必须为 HH:mm", e.Field())
                default:
                    return fmt.Errorf("字段 %s 校验失败：%s", e.Field(), e.Tag())
                }
            }
        }
        return fmt.Errorf("配置校验错误: %v", err)
    }
    // 模式专项校验
    var errs []error
    switch rc.Mode {
    case "dynamic":
        if rc.MaxDays <= 0 {
            errs = append(errs, errors.New("动态模式必须设置有效MaxDays（需大于0）"))
        }
        if len(rc.TimeSlots) == 0 {
            errs = append(errs, errors.New("动态模式必须配置至少一个时段"))
        }
    case "static":
        if len(rc.AllowedDates) == 0 {
            errs = append(errs, errors.New("静态模式必须配置 allowedDates 列表"))
        }
        if len(rc.TimeSlots) == 0 {
            errs = append(errs, errors.New("静态模式必须配置 timeSlots 全局时段"))
        }
    case "custom":
        if len(rc.AllowedDates) == 0 {
            errs = append(errs, errors.New("自定义模式必须配置 allowedDates 列表"))
        }
        for i, dateSlot := range rc.AllowedDates {
            if len(dateSlot.TimeSlots) == 0 {
                errs = append(errs, fmt.Errorf("自定义模式下日期项[%d]必须配置时段", i+1))
            }
        }
    }
    // 日期格式校验
    for i, dateSlot := range rc.AllowedDates {
        if _, err := time.Parse("2006-01-02", dateSlot.Date); err != nil {
            errs = append(errs, fmt.Errorf("allowedDates[%d]日期格式错误：%s", i+1, dateSlot.Date))
        }
    }
    // 时段逻辑校验
    slotMap := make(map[string]bool)
    for i, slot := range rc.TimeSlots {
        start, _ := time.Parse("15:04", slot.Start)
        end, _ := time.Parse("15:04", slot.End)
        if !end.After(start) {
            errs = append(errs, fmt.Errorf("全局时段[%d]：结束时间必须晚于开始时间", i+1))
        }
        key := fmt.Sprintf("%s-%s", slot.Start, slot.End)
        if slotMap[key] {
            errs = append(errs, fmt.Errorf("全局时段冲突：%s", key))
        }
        slotMap[key] = true
    }
    // 合并错误
    if len(errs) > 0 {
        return errors.Join(errs...)
    }
    return nil
}

// 判断字段是否为图片、多选、多行文本、子表类型
func isImageField(fieldName string) bool {
    selectzd := []string{"textarea", "muselect", "checkbox", "subtable"}
    return strings.HasPrefix(fieldName, "image") || InArray(fieldName, selectzd)
}

//动态创建表
func CreatePhysicalTable(tx *gorm.DB, form FormSchema) error {
    // 检查表名是否合法
    if !validTableNameRegex.MatchString(form.TableName) {
        return fmt.Errorf("数据表名不合法: %s", form.TableName)
    }
    columns := []string{"id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY"}
    // 校验字段配置
    configs, err := ValidateFieldConfigs(form); 
    if err != nil {
        return err
    }
    var cxtj []string
	if err := json.Unmarshal([]byte(form.Cxtj), &cxtj); err != nil {
        return fmt.Errorf("查询条件配置解析失败: %v", err)
    }
    for key := range configs {
        if len(cxtj) > 0 && InArray(key, cxtj) {
            // 查询条件的字段要设置为VARCHAR(255)
            columns = append(columns, fmt.Sprintf("`%s` VARCHAR(255) NULL", key))
        } else {
            // 否则使用TEXT类型字段
            columns = append(columns, fmt.Sprintf("`%s` TEXT NULL", key))
        }
    }
    // 添加预约专用字段
    columns = append(columns,
        "sequence_number INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '时段内排号顺序'",
        "reservation_date DATE NULL COMMENT '预约日期'",
        "slot_start TIME NULL COMMENT '时段开始'",
        "slot_end TIME NULL COMMENT '时段结束'",
        "verification_code VARCHAR(255) NOT NULL DEFAULT '' COMMENT '核销码'",
        "used TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否已核销'",
        "used_time DATETIME NULL COMMENT '核销时间'",
        "operator_id INT UNSIGNED NULL COMMENT '核销操作人ID'",
    )
    // 添加系统字段
    columns = append(columns,
        "query_count INT UNSIGNED DEFAULT 0",           // 查询次数
        "is_locked TINYINT(1) DEFAULT 0",               // 是否锁定
        "cjz INT UNSIGNED DEFAULT 0",                   // 填报人
        "created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP",
        "updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
    )
    // 生成 CREATE TABLE SQL 语句
    sql := fmt.Sprintf("CREATE TABLE IF NOT EXISTS `%s` (\n  %s\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;", form.TableName, strings.Join(columns, ",\n  "))
    // 打印 SQL，帮助调试
    fmt.Println("Executing SQL: ", sql)
    // 执行 SQL 创建表
    if err := tx.Exec(sql).Error; err != nil {
        return fmt.Errorf("error creating physical table: %v", err)
    }
    return nil
}

// 字段操作统一方法
func handleColumn(tx *gorm.DB, table string, action, key string) error {
    if !validColumnRegex.MatchString(key) {
        return fmt.Errorf("invalid column name: %s", key)
    }
    sql := ""
    switch action {
    case "ADD":
        sql = fmt.Sprintf("ALTER TABLE `%s` ADD COLUMN `%s` TEXT NULL", table, key)
    case "DROP":
        sql = fmt.Sprintf("ALTER TABLE `%s` DROP COLUMN `%s`", table, key)
    default:
        return fmt.Errorf("invalid alter action")
    }
    // 打印执行的 SQL，方便调试
    fmt.Println("Executing SQL: ", sql)
    return tx.Exec(sql).Error
}

// 字段校验操作方法
func CheckRules(tx *gorm.DB, oldForm, newForm FormSchema) error {
    oldFields, err := ParseFieldConfig(oldForm.Fields)
	if err != nil {
		return fmt.Errorf("旧字段配置解析错误: %v", err)
	}
	newFields, err := ParseFieldConfig(newForm.Fields)
	if err != nil {
		return fmt.Errorf("新字段配置解析错误: %v", err)
	}
    // 处理新增字段
    for key := range newFields {
        if _, exists := oldFields[key]; !exists {
            // 如果旧字段中没有该字段，则添加新字段
            if err := handleColumn(tx, oldForm.TableName, "ADD", key); err != nil {
                return fmt.Errorf("添加字段失败: %v", err)
            }
        }
    }
    // 处理删除字段
    for key := range oldFields {
        if _, exists := newFields[key]; !exists {
            // 如果新字段中没有该字段，则删除该字段
            if err := handleColumn(tx, oldForm.TableName, "DROP", key); err != nil {
                return fmt.Errorf("删除字段失败: %v", err)
            }
        }
    }
    return nil
}

// 检查查询条件字段并修改字段类型
func CheckAndUpdateFields(tx *gorm.DB, form FormSchema) error {
    var cxtj []string
    if err := json.Unmarshal([]byte(form.Cxtj), &cxtj); err != nil {
        return fmt.Errorf("查询条件配置解析失败: %v", err)
    }
    if len(cxtj) > 0 {
        tableName := form.TableName
        if tableName == "" {
            return fmt.Errorf("TableName为空")
        }
        // 遍历查询条件字段，如果字段存在且类型为TEXT，修改为VARCHAR(255)
        for _, column := range cxtj {
            if !checkIndex(tx, tableName, column) {
                fmt.Println(tableName + " 添加索引 " + column)
                addIndex(tx, tableName, column) // 添加索引
            }
        }
        // 查询当前表的索引信息
        var indexes []struct {
            ColumnName string `gorm:"column:COLUMN_NAME"`
        }
        if err := tx.Raw("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND INDEX_NAME != 'PRIMARY'", tableName).Find(&indexes).Error; err != nil {
            return fmt.Errorf("查询索引失败： %s", err)
        }
        // 处理已有索引的列
        fmt.Println("当前表的索引：", indexes)
        for _, index := range indexes {
            column := index.ColumnName // 不做大小写转换
            // 如果字段不在 cxtj 中，删除索引
            if !InArray(column, cxtj) {
                fmt.Println(tableName + " 删除索引 " + column)
                removeIndex(tx, tableName, column)
            }
        }
    }
    return nil
}

// 检查索引是否存在
func checkIndex(tx *gorm.DB, tableName string, columnName string) bool {
    var count int
    indexName := tableName + "_" + columnName + "_index" // 使用原始大小写的列名
    query := `SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND INDEX_NAME = ?`
    if err := tx.Raw(query, tableName, indexName).Scan(&count).Error; err != nil {
        fmt.Println("查询索引失败:", err)
        return false
    }
    return count > 0
}

// 添加索引
func addIndex(tx *gorm.DB, tableName string, columnName string) {
    if columnName != "id" {
        indexName := tableName + "_" + columnName + "_index" // 使用原始大小写的列名
        columnType := getColumnType(tx, tableName, columnName)
        if strings.Contains(columnType, "text") {
            // 为 TEXT 字段指定前缀长度 255
            tx.Exec(fmt.Sprintf("ALTER TABLE `%s` ADD INDEX `%s` (`%s`(255))", tableName, indexName, columnName))
        } else {
            tx.Exec(fmt.Sprintf("ALTER TABLE `%s` ADD INDEX `%s` (`%s`)", tableName, indexName, columnName))
        }
    }
}

// 获取字段类型
func getColumnType(tx *gorm.DB, tableName string, columnName string) string {
    var columnType string
    query := `SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ? AND COLUMN_NAME = ?`
    if err := tx.Raw(query, tableName, columnName).Scan(&columnType).Error; err != nil {
        fmt.Println("查询字段类型失败:", err)
        return ""
    }
    return columnType
}

// 删除索引
func removeIndex(tx *gorm.DB, tableName string, columnName string) {
    if columnName != "id" {
        indexName := tableName + "_" + columnName + "_index" // 使用原始大小写的列名
        // 如果索引存在则删除
        if checkIndex(tx, tableName, columnName) {
            tx.Exec(fmt.Sprintf("DROP INDEX `%s` ON `%s`", indexName, tableName))
        }
    }
}